﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using static System.Formats.Asn1.AsnWriter;
using static System.Net.Mime.MediaTypeNames;

namespace SuperKit
{
    public class MediaCombiner {
        public int Width { get; }
        public int Height { get; }
        public float BytesRate { get; }
        public string FullName { get; }

        public MediaCombiner(string path, int width, int height, float bytesRate) {
            this.Width = width;
            this.Height = height;
            this.BytesRate = bytesRate;
            FullName = path;
        }

        public string GetTargetPath() {
            string pureName = Path.GetFileNameWithoutExtension(FullName);
            string ext = Path.GetExtension(FullName);
            return Path.Combine(Path.GetDirectoryName(FullName), $"{pureName}_MIX{ext}");
        }

        public string GetTextPath() {
            string pureName = Path.GetFileNameWithoutExtension(FullName);
            return Path.Combine(Path.GetDirectoryName(FullName), $"{pureName}_MIX.txt");
        }

        public string SizeStr {
            get {
                return Width == 0 || Height == 0 ? "" : $"-s {Width}x{Height}";
            }
        }

        public string ByteStr {
            get {
                return BytesRate > 0.01f ? $"-b:v {BytesRate}M" : "";
            }
        }

        public string GetGeneral() {
            return $"-f concat -safe 0 -hwaccel_output_format cuda -i \"{GetTextPath()}\" {SizeStr} {ByteStr} -y  -vcodec h264_nvenc \"{GetTargetPath()}\"";
        }

        public string GetCopy() {
            return string.Format("-f concat -safe 0 -i \"{0}\" -c copy \"{1}\"", GetTextPath(), GetTargetPath());
        }
    }

    public class MediaCutter {
        public MediaCutter(string path, int index, int start, int end) {
            FullName = path;
            Index = index;
            Start = start;
            End = end;
        }

        public string FullName { get; }
        public int Index { get; }
        public int Start { get; }
        public int End { get; }

        public string GetTargetPath() {
            string pureName = Path.GetFileNameWithoutExtension(FullName);
            string ext = Path.GetExtension(FullName);
            return Path.Combine(Path.GetDirectoryName(FullName), $"{pureName}_{Index + 1}{ext}");
        }

        public string GetCommand() {
            string start = Media.FormatTime(Start);
            string end = Media.FormatTime(End);

            string template = "-ss {0} -to {1} -accurate_seek -i \"{2}\" -c copy -avoid_negative_ts 1 \"{3}\" -y";
            return string.Format(template, start, end, FullName, GetTargetPath());
        }

        public void Execute() {
            Media.ExecuteFFMpeg(GetCommand());
        }
    }
    public class Media {
        public delegate void Output(string msg);
        public delegate void FinishCallback(string msg);

        public const double KB = 1024;
        public const double MB = 1024 * KB;
        public const double GB = 1024 * MB;

        public static void Execute(List<MediaCutter> cutters, Output output, FinishCallback callback = null) {
            Thread t = new(() => {                
                foreach (var cutter in cutters) {
                    output($"开始处理：{cutter.GetTargetPath()}");
                    cutter.Execute();
                }
                output("——处理完成——");
                callback?.Invoke(string.Empty);
            });
            t.Start();
        }


        public static void ExecuteCombine(List<MediaCombiner> combiners, bool isCopy, Output output, FinishCallback callback = null) {
            MediaCombiner header = combiners[0];
            output($"开始处理：{header.GetTargetPath()}");

            StringBuilder sb = new StringBuilder();
            foreach (var i in combiners) {
                sb.Append(string.Format("file '{0}'\r\n", i.FullName));
            }
            
            string cmd = isCopy ? header.GetCopy() :  header.GetGeneral();

            Thread t = new Thread(() => {
                File.WriteAllText(header.GetTextPath(), sb.ToString());
                ExecuteFFMpeg(cmd);
                File.Delete(header.GetTextPath());

                output("——处理完成——");
                callback?.Invoke(string.Empty);
            });
            t.Start();
        }

        public static string FormatTime(int second) {
            int secondLeft = second % 60;
            int minute = second / 60;
            int minuteLeft = minute % 60;
            int hour = minute / 60;
            return string.Format("{0:D2}:{1:D2}:{2:D2}.0", hour, minuteLeft, secondLeft);
        }

        public static string FormatSize(double size) {
            var kb = size / KB;
            if (kb < 1024) {
                return $"{kb:N0} KB";
            }

            var mb = size / MB;
            if (mb < 1024) {
                if (mb < 10) {
                    return $"{mb:N2} MB";
                }else if (mb < 100) {
                    return $"{mb:N1} MB";
                }
                return $"{mb:N0} MB";
            }

            var gb = size / GB;
            return $"{gb:N2} GB";
        }

        public static string ExecuteFFMpeg(string cmd) {
            Process p = new Process();
            //设置要启动的应用程序
            p.StartInfo.FileName = "C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe";
            p.StartInfo.Arguments = cmd;
            //是否使用操作系统shell启动
            p.StartInfo.UseShellExecute = false;
            // 接受来自调用程序的输入信息
            //p.StartInfo.RedirectStandardInput = true;
            //输出信息
            //p.StartInfo.RedirectStandardOutput = true;
            // 输出错误
            //p.StartInfo.RedirectStandardError = true;
            //不显示程序窗口
            p.StartInfo.CreateNoWindow = false;
            //启动程序
            p.Start();

            //等待程序执行完退出进程
            p.WaitForExit();
            p.Close();

            return Kit.EMPTY;
        }



    }
}
